#include <Arduino.h>
#include "MouseMotion.h"
#include "Adafruit_TinyUSB.h"
#ifndef MH_ER
#define MH_ER "ER\n"
#endif
#ifndef MH_OK
#define MH_OK "OK\n"
#endif
#ifndef PI
#define PI 3.141592653
#endif
// 声明外部 desc_hid_report_combined 对象
extern Adafruit_USBD_HID usb_hid;

void mouseScroll(uint8_t scroll,int direction){
  int v = 0;
  if(direction>0){
      v =  1;
  }else if(direction==0){
      v =  -1;
  }else{
      v = -1;
  }
  uint8_t report[4] = {0}; // 第0个字节是 Report ID = 1
  report[0] = 0x00;        // 按钮
  report[1] = 0x00;        // X
  report[2] = 0x00;        // Y
  report[3] = (int8_t)scroll*v;  // 滚轮向下

  usb_hid.sendReport(HID_RELATIVE_MOUSE_ID, report, sizeof(report));
  delay(16);

  // 发送清零报告释放滚轮状态
  report[3] = 0;
  usb_hid.sendReport(HID_RELATIVE_MOUSE_ID, report, sizeof(report));
}

void mouseClick(int key){
   mousePress(key);
   delay(60);
   mouseRelease(key);
}

void mousePress(int key){
  // 构造一个报告，将指定按钮按下，X/Y移动为 0
  uint8_t report[] = {
    key,     // Button: 例如 0x01 (左键)，0x02 (右键)，0x04 (中键)
    0x00,    // X Movement: 0
    0x00,     // Y Movement: 0
    0x00
  };
  usb_hid.sendReport(HID_RELATIVE_MOUSE_ID, report, sizeof(report));  // 发送按下按钮的报告
}

void pressAndMoveAbsolute(int key,int px, int py){
    uint8_t report[] = {
    key,  // Buttons: bitmask (e.g., 1 = left click)
    (uint8_t)px, // X movement
    (uint8_t)py,  // Y movement
    0x00
  };
  usb_hid.sendReport(HID_RELATIVE_MOUSE_ID,report, sizeof(report));
}

void mouseRelease(int key){
   // 构造一个报告，释放指定按钮，X/Y移动为 0
  uint8_t report[] = {
    0x00,    // Button: 0x00 (所有按钮松开)
    0x00,    // X Movement: 0
    0x00,     // Y Movement: 0
    0x00
  };
  usb_hid.sendReport(HID_RELATIVE_MOUSE_ID, report, sizeof(report));  // 发送释放按钮的报告
}

void moveRelative(int px, int py,int delayMs){
   uint8_t report[] = {
    0x00,  // Buttons: bitmask (e.g., 1 = left click)
    (uint8_t)px, // X movement
    (uint8_t)py,  // Y movement
    0x00
  };
  usb_hid.sendReport(HID_RELATIVE_MOUSE_ID,report, sizeof(report));
}
void moveAbsolute(int px, int py,int screen_width,int screen_height) {
    const float hid_max = 32767.0;
    uint16_t x = (uint16_t)(round(px * (hid_max / (screen_width - 1))));
    uint16_t y = (uint16_t)(round(py * (hid_max / (screen_height - 1))));
    uint8_t report[5];
    report[0] = 0x00; // no button
    report[1] = x & 0xFF;
    report[2] = (x >> 8) & 0xFF;
    report[3] = y & 0xFF;
    report[4] = (y >> 8) & 0xFF;
    usb_hid.sendReport(HID_ABS_MOUSE_ID, report, sizeof(report));
}

int calculateSteps(int x0, int y0, int x1, int y1, float pixelsPerStep) {
  float dx = x1 - x0;
  float dy = y1 - y0;
  float distance = sqrt(dx * dx + dy * dy);
  return min(max(1, int(distance / pixelsPerStep)),int(random(80, 100)));
}

void moveBezierAutoSteps(int x0, int y0, int x2, int y2,int screen_width,int screen_height, float pixelsPerStep, int delayMs) {
  // 计算步骤数
  int steps = calculateSteps(x0, y0, x2, y2, pixelsPerStep);
  //Serial0.printf("Steps: (%d)\n", steps);

  float dx = x2 - x0;
  float dy = y2 - y0;
  float curveFactor = random(20, 50) / 100.0;  // 随机值范围：0.2 到 0.5

  // 计算法线方向，避免除零错误
  float len = sqrt(dx * dx + dy * dy);
  if (len < 1.0) len = 1.0;  // 避免除零
  float nx = -dy / len;
  float ny = dx / len;

  // 计算控制点，限制偏移
  float mx = x0 + dx / 2;
  float my = y0 + dy / 2;
  // 根据随机方向调整弯曲方向
  float offset = len * curveFactor * (random(0, 2) ? 1 : -1);

  // 限制偏移在合理范围内
  float max_offset = len * 0.25; // 偏移值不超过曲线长度的四分之一
  offset = constrain(offset, -max_offset, max_offset);

  // 计算控制点
  float x1 = mx + nx * offset;
  float y1 = my + ny * offset;

  // 限制控制点在屏幕范围内
  x1 = constrain(x1, 0, screen_width - 1);
  y1 = constrain(y1, 0, screen_height - 1);

  for (int i = 0; i <= steps; i++) {
    float t = (float)i / steps;
    float u = 1 - t;

    // 计算贝塞尔曲线上的点，限制在屏幕范围内
    float x = u * u * x0 + 2 * u * t * x1 + t * t * x2;
    float y = u * u * y0 + 2 * u * t * y1 + t * t * y2;

     // 生成小范围的随机抖动
    float jitter_x = random(-5, 5); // 水平方向的抖动
    float jitter_y = random(-5, 5); // 垂直方向的抖动

    // 加入抖动
    x += jitter_x;
    y += jitter_y;

    // 限制插值结果在屏幕范围内
    x = constrain(x, 0, screen_width - 1);
    y = constrain(y, 0, screen_height - 1);

    // 移动到这个位置
    moveAbsolute((int)x, (int)y, screen_width, screen_height);
    delay(delayMs);
  }
  //moveAbsolute(x2, y2, screen_width, screen_height);
}

void moveArc(int x0, int y0, int x2, int y2, int screen_width, int screen_height, int delayMs) {
    // 计算步骤数
    int steps = calculateSteps(x0, y0, x2, y2,  10.0);
    // 计算圆弧的中心
    int cx = (x0 + x2) / 2;
    int cy = (y0 + y2) / 2;

    // 计算半径
    float radius = sqrt(pow(x2 - cx, 2) + pow(y2 - cy, 2));

    // 计算圆弧的起始和结束角度
    float start_angle = atan2(y0 - cy, x0 - cx);
    float end_angle = atan2(y2 - cy, x2 - cx);

    // 随机选择顺时针或逆时针方向
    int direction = random(0, 2) == 0 ? 1 : -1; // 1表示顺时针，-1表示逆时针

    // 步数插值
    for (int i = 0; i <= steps; i++) {
        float t = (float)i / steps;

        // 计算圆弧上的当前角度
        float angle = start_angle + direction * t * (end_angle - start_angle);

        // 计算圆弧路径上的点
        int x = cx + radius * cos(angle);
        int y = cy + radius * sin(angle);

        // 随机抖动：在x和y方向上加入一个随机值
        x += random(-2, 3); // ±2像素的抖动范围
        y += random(-2, 3); // ±2像素的抖动范围

        // 限制范围
        x = constrain(x, 0, screen_width - 1);
        y = constrain(y, 0, screen_height - 1);

        // 移动到该位置
        moveAbsolute(x, y, screen_width, screen_height);
        delay(delayMs);  // 控制延时
    }
    moveAbsolute(x2, y2, screen_width, screen_height);
}

void moveSineWave(int x0, int y0, int x2, int y2, int screen_width, int screen_height, int delayMs){
  // 计算步骤数
  int steps = calculateSteps(x0, y0, x2, y2, 10.0);
  float dx = x2 - x0;
  float dy = y2 - y0;

  // 随机方向：通过设置方向变量来决定正弦波是向上波动还是向下波动
  int direction = random(0, 2) == 0 ? 1 : -1;  // 1 表示向上，-1 表示向下

  // 步数插值
  for (int i = 0; i <= steps; i++) {
        float t = (float)i / steps;

        // 生成正弦波形，乘上方向
        float wave = sin(t * 2 * PI) * 5 * direction;  // 波动幅度为5像素，并根据方向决定是向上还是向下

        // 计算目标位置
        int x = x0 + t * dx;
        int y = y0 + t * dy + wave;

        // 随机抖动：在x和y方向上加入一个小的随机值
        x += random(-2, 3);  // ±2像素的抖动范围
        y += random(-2, 3);  // ±2像素的抖动范围

        // 限制范围
        x = constrain(x, 0, screen_width - 1);
        y = constrain(y, 0, screen_height - 1);

        // 移动到该位置
        moveAbsolute(x, y, screen_width, screen_height);
        delay(delayMs);  // 控制延时
  }
  moveAbsolute(x2, y2, screen_width, screen_height);
}
void moveParabola(int x0, int y0, int x2, int y2, int screen_width, int screen_height, int delayMs){
  // 计算步骤数
  int steps = calculateSteps(x0, y0, x2, y2, 10.0);
  // 计算起始点和结束点的水平和垂直距离
  float dx = x2 - x0;
  float dy = y2 - y0;

  // 随机方向：通过设置方向变量来决定抛物线的弯曲方向
  int direction = random(0, 2) == 0 ? 1 : -1;  // 1 表示向上，-1 表示向下

  // 步数插值
  for (int i = 0; i <= steps; i++) {
      float t = (float)i / steps;

      // 计算抛物线上的当前点
      float x = x0 + t * dx;
      float y = y0 + t * dy + direction * (4 * t * (1 - t) * dy);  // 标准抛物线公式

      // 随机抖动：在x和y方向上加入一个小的随机值
      x += random(-2, 3);  // ±2像素的抖动范围
      y += random(-2, 3);  // ±2像素的抖动范围

      // 限制范围
      x = constrain(x, 0, screen_width - 1);
      y = constrain(y, 0, screen_height - 1);

      // 移动到该位置
      moveAbsolute(x, y, screen_width, screen_height);
      delay(delayMs);  // 控制延时
  }
  moveAbsolute(x2, y2, screen_width, screen_height);
}


void moveRandomJitter(int x0, int y0, int x2, int y2, int screen_width, int screen_height, int delayMs){
  // 计算步骤数
  int steps = calculateSteps(x0, y0, x2, y2, 10.0);
  float dx = x2 - x0;
    float dy = y2 - y0;
    for (int i = 0; i <= steps; i++) {
        float t = (float)i / steps;
        
        // 计算目标位置
        int x = x0 + t * dx;
        int y = y0 + t * dy;
        
        // 添加随机抖动
        x += random(-5, 5);
        y += random(-5, 5);
        
        // 限制范围
        x = constrain(x, 0, screen_width - 1);
        y = constrain(y, 0, screen_height - 1);
        
        // 移动到该位置
        moveAbsolute(x, y, screen_width, screen_height);
        delay(delayMs);
    }
  moveAbsolute(x2, y2, screen_width, screen_height);
}
void moveSpiral(int x0, int y0, int x2, int y2, int screen_width, int screen_height,int delayMs){
  // 计算步骤数
  int steps = calculateSteps(x0, y0, x2, y2, 10.0);
    float totalRadius = sqrt(pow(x2 - x0, 2) + pow(y2 - y0, 2)) / 2;
    float angle = 0;
    int direction = random(0, 2) == 0 ? 1 : -1;

    for (int i = 0; i <= steps; i++) {
        float t = (float)i / steps;

        // 沿主线方向计算中心点位置
        float cx = x0 + t * (x2 - x0);
        float cy = y0 + t * (y2 - y0);

        // 螺旋半径从大到小（也可以反过来）
        float radius = totalRadius * (1.0 - t);

        // 当前角度
        angle += direction * (PI / 6);  // 控制螺旋密度

        // 螺旋偏移
        float x = cx + radius * cos(angle);
        float y = cy + radius * sin(angle);

        // 加入抖动
        x += random(-2, 3);
        y += random(-2, 3);

        // 限制屏幕范围
        x = constrain(x, 0, screen_width - 1);
        y = constrain(y, 0, screen_height - 1);

        moveAbsolute((int)x, (int)y, screen_width, screen_height);
        delay(delayMs);
    }
  moveAbsolute(x2, y2, screen_width, screen_height);
}

void keyPress(int keyCode){
  uint8_t keys[6] = { 0 };
  keys[0] = (uint8_t)keyCode;
  usb_hid.keyboardReport(HID_KEYBORAD_ID, 0, keys);
}
void keyGroupPress(int keyCodes,...){
  uint8_t modifier = 0;
  uint8_t keys[6] = {0};
  uint8_t keyIndex = 0;
  va_list args;
  va_start(args, keyCodes);
  for (int i = 0; i < keyCodes; ++i) {
    uint8_t code = va_arg(args, int);
    if (code >= 0xE0 && code <= 0xE7) {
      modifier |= (1 << (code - 0xE0));  // 将对应位设置为 1
    } else if (keyIndex < 6) {
      keys[keyIndex++] = code;           // 非 modifier 键放入 key array
    }
  }
  va_end(args);
  usb_hid.keyboardReport(HID_KEYBORAD_ID, modifier, keys);
}

void modifyKey(uint8_t modifier, uint8_t keyCode){
  uint8_t keys[6] = {0};  // 最多支持6个按键
  keys[0] = keyCode;
  usb_hid.keyboardReport(HID_KEYBORAD_ID, modifier, keys);
}

void keyRelease(){
  usb_hid.keyboardRelease(HID_KEYBORAD_ID);
}

void enterKey(int keyCode){
  uint8_t keys[6] = { 0 };
  keys[0] = keyCode;
  usb_hid.keyboardReport(HID_KEYBORAD_ID, 0x00,keys);
  delay(8);
  usb_hid.keyboardRelease(HID_KEYBORAD_ID);
}
